Scala Cookbook翻译 Chapter 1.Strings 第一部分

1.0 总体介绍

  • Scala中的String类型就是Java中的String,所以可以使用Java中String的一些方法。
  • Scala提供了隐式转换,String也可以使用StringOps类的所有方法,此时可以把String当做字符序列,

    • 可以利用foreach迭代打印每个字符:”hello”.foreach(println)
    • 通过for循环打印每个字符:for (c <- “hello”) println(c)
    • 也可以当做字节序列:”hello”.getBytes.foreach(println) //104 101 108 108 111(十进制ASCII编码)
    • 使用过滤器:val result = “hello world”.filter(_ != ‘l’) //heo word
  • 实际上这些方法并不都是来自类StringOps,有的还来自StringLike和WrappedString等,可到api文档查看String到StringOps的隐式转换。

1.1 字符串相等

  • Scala中通过 == 比较两个对象是否相等,这点与Java不同,Java中是通过equals比较两个对象。
  • Scala中==方法定义在类AnyRef中,首先会检查是否为null,然后再调用第一个对象的equals方法比较两个对象,所以比较两个字符串时不需要再进行null值判断。
  • 在Scala语法中,从来不会使用空值,但是不排除使用java类库或者其他库时出现null的可能。

    val s1 = "Hello"
    val s2 = "Hello"
    val s3 = "H" + "ello"
    s1 == s2 //true
    s1 == s3 //true
    
  • 忽略大小写判断字符串相等

    val s1: String = null
    val s2: String = null
    s1.toUpperCase == s2.toUpperCase //此刻报空指针错误,正确姿势如下
    
    val a = "Marisa"
    val b = "marisa"
    a.equalsIgnoreCase(b)  //true
    a.toUpperCase == b.toUpperCase //true
    

1.2 创建多行字符串

  • 在Scala中通过3个双引号创建多行字符串,但是下例打印出来第二及之后行行首以白色空格开始

    val foo = """This is
    a multiline
    String"""
    
    ------------//输出如下
    This is
        a multiline
        String
    
  • 可通过以下几种方法解决这个问题

    • 每行都和第一行对齐
    • 在多行字符串的最后添加stripMargin方法,第一行之外的每行行首以分隔符|开始

      val speech = """Four score and
      |seven years ago""".stripMargin
      
    • 如果不喜欢|分隔符, 可通过stripMargin方法使用任何字符

      val speech = """Four score and
      #seven years ago""".stripMargin('#')
      
      ------------//输出如下
      Four score and
      seven years ago
      
    • 上述多行字符串实际是在行末有一个隐藏的\n换行字符,如果要把多行转成一行,可在stripMargin方法之后调用replaceAll方法,用空格替换换行符

      val speech = """Four score and
      |seven years ago
      |our fathers""".stripMargin.replaceAll("\n", "")
      
      ------------//输出如下
      Four score and seven years ago our fathers
      
    • 多行字符串有一个好处就是可以直接输出单双引号,而不用可以避免

      val s = """This is known as a
      |"multiline" string
      |or 'heredoc' syntax.""". stripMargin.replaceAll("\n", " ")
      
      ------------//输出如下
      This is known as a "multiline" string or 'heredoc' syntax.
      

1.3 分隔字符串

  • 通过split方法分隔字符串,返回字符串元素的Array数组

    scala> "hello world".split(" ")
    res0: Array[java.lang.String] = Array(hello, world)
    
    scala> "hello world".split(" ").foreach(println)
    hello
    world
    
  • 对于下面的例子最好使用map方法调用trim去除左右两边的空格

    scala> val s = "eggs, milk, butter, Coco Puffs"
    s: java.lang.String = eggs, milk, butter, Coco Puffs
    // 1st attempt
    scala> s.split(",")
    res0: Array[java.lang.String] = Array(eggs, " milk", " butter", " Coco Puffs")  //此处引号的存在是因为空格的存在,所以必须用引号指出来
    
    ---------正确姿势如下
    // 2nd attempt, cleaned up
    scala> s.split(",").map(_.trim)
    res1: Array[java.lang.String] = Array(eggs, milk, butter, Coco Puffs)
    
  • 匹配空白字符作为分隔符

    scala> "hello world, this is Al".split("\\s+") //正则表达,见下
    res0: Array[java.lang.String] = Array(hello, world,, this, is, Al)
    
  • split方法有些版本来自Java中String类,有的来自Scala中StringLike类,比如用字符串表示来自String,用字符表示来自StringLike

    // split with a String argument
    scala> "hello world".split(" ")
    res0: Array[java.lang.String] = Array(hello, world)
    
    // split with a Char argument
    scala> "hello world".split(' ')
    res1: Array[String] = Array(hello, world)
    

正则表达\s+详解

  • 正则表达式中\s匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]

    \f -> 匹配一个换页
    \n -> 匹配一个换行符
    \r -> 匹配一个回车符
    \t -> 匹配一个制表符
    \v -> 匹配一个垂直制表符
    
  • 而“\s+”则表示匹配任意多个上面的字符。另因为反斜杠在Java里是转义字符,所以在Java里,我们要这么用“\s+”.

    非常好用的正则表达式”\s+” - 匹配任意空白字符

1.4 在字符串中插入变量

1.4.1 在字符串中使用变量

  • 2.10开始,Scala通过字符串插值可以有Perl,PHP,Puby语言一样的效果。如下需要字母 s 在字符串之前,字符串包含变量,变量前用 $ 符表示

    scala> val name = "Fred"
    name: String = Fred
    scala> val age = 33
    age: Int = 33
    scala> val weight = 200.00
    weight: Double = 200.0
    
    scala> println(s"$name is $age years old, and weights $weight pounds.")
    Fred is 33 years old, and weights 200.0 pounds.
    
  • 当字母 s 在字符串之前时,就创建了一个可处理的字符串文本。Scala中规定,当字母 s 出现在任何字符串之前都允许直接在字符串中使用变量。

1.4.2 在字符串中使用表达式

  • 通过通过${}在字符串中插入算术表达式

    //算术表达式
    scala> println(s"Age next year: ${age + 1}")
    Age next year: 34
    
    //相等表达式
    scala> println(s"You are 33 years old: ${age == 33}")
    You are 33 years old: true
    
  • 当打印对象的字段时需要用大括号包裹

    //正确方式
    scala> case class Student(name: String, score: Int)
    defined class Student
    scala> val hannah = Student("Hannah", 95)
    hannah: Student = Student(Hannah,95)
    scala> println(s"${hannah.name} has a score of ${hannah.score}")
    Hannah has a score of 95
    
    //错误方式,此处是调用类的toString方法输出hannah变量
    scala> println(s"$hannah.name has a score of $hannah.score")
    Student(Hannah,95).name has a score of Student(Hannah,95).score
    

1.4.3 字母s是一个方法

  • 字母s是一个方法,尽管看起来比直接在字符串中插入变量要麻烦一点,但是它至少有以下两个优点:
    • Scala提供了更多现成的插补函数
    • 可以自定义插补函数

1.4.4 插补器f(使用printf格式),可以格式化输出值

  • 输出两个小数位数

    scala> println(f"$name is $age years old, and weighs $weight%.2f pounds.")
    Fred is 33 years old, and weighs 200.00 pounds.
    
  • 完全去掉小数位

    scala> println(f"$name is $age years old, and weighs $weight%.0f pounds.")
    Fred is 33 years old, and weighs 200 pounds.
    
  • 把格式化的结果插补给另一个变量,如同在其他语言中调用sprintf一样(sprintf:字符串格式化命令,主要功能是把格式化的数据写入某个字符串中, sprintf函数的功能与printf函数的功能基本一样,只是它把结果输出到指定的字符串中了)

    scala> val out = f"$name, you weigh $weight%.0f pounds."
    out: String = Fred, you weigh 200 pounds.
    

1.4.5 插补器raw

  • 用于显示字符串中所有文本,原封不动的显示出来

    scala> "foo\nbar"
    res0: String =
    foo
    bar
    
    scala> s"foo\nbar"
    res1: String =
    foo
    bar
    
    scala> raw"foo\nbar"
    res1: String = foo\nbar
    
  • 当想要避免如\n转换成换行符情况的字符序列时将非常有用

1.4.6 自定义插补器

  • 比较复杂,以后回顾看以下官方文档
  • 字符串插补器在Scala2.10时对模式匹配语句不起作用,此功能计划在2.11上

    String Interpolation

1.4.7 2.10版本之前解决办法

  • 如果因为某些原因需要使用2.10之前的版本,可以用format方法作用在字符串上,如下

    scala> val name = "Fred"
    name: java.lang.String = Fred
    
    scala> val age = 33
    age: Int = 33
    
    scala> val s = "%s is %d years old".format(name, age)
    s: String = Fred is 33 years old
    
    scala> println("%s is %d years old".format(name, age))
    Fred is 33 years old
    
  • 和字符串插补器一样,这种方法也可以使用在任何需要格式化字符串的地方,比如toString方法

    override def toString: String =
        "%s %s, age %d".format(firstName, lastName, age)
    
  • 无论使用哪种方法,都可以使用printf说明符格式化变量。以下是常用格式符

    格式符  描述
    %c  字符型
    %d  十进制整数 Decimal number (integer, base 10)
    %e  浮点型指数
    %f  浮点数
    %i  十进制整数 Integer (base 10)
    %o  八进制整数
    %s  字符串
    %u  十进制无符号整数
    %x  十六进制整数
    %%  打印百分比字符
    \%  打印百分比字符
    
  • 查看更多

    更多printf格式符 format specifiers and examples
    java中更多例子和细节 Oracle Formatter page
    官方文档 The official Scala String Interpolation documentation